# -*- coding: utf-8 -*-
"""
Module Description: 赛季榜单相关

Date: 2020/12/18
Author: sxj
"""

from pyshare.utils.share_rank_utils import normal_rank, MAX_MODULUS, OFFSET_BASE_LIMIT
from pyshare.utils.time_utils import getCT, addTimes, strToDatetime, getCountdown
from pyshare.const.enums.cross_season_enum import CrossSeasonRankKey
from pyshare.utils.season_utils import get_season_base_info, get_day_of_season
from pyshare.dal.repository_impl import CrossSeasonActivityRepository
from pyshare.redis_cache.cross.cross_season_cache import CrossSeasonHashCache


class SeasonRankUtils(object):
    """
    排行榜基础方法
    """

    @staticmethod
    def get_sub_time(season_id, cur_time=None, time_reverse=True):
        """
        获取榜单第二排序规则时间差
        时间差为当前时间与赛季start_time差值的秒数
        :param time_reverse: True: 先到达相同某分数的玩家排名靠前
        """
        if not cur_time:
            cur_time = getCT()

        season_record = CrossSeasonActivityRepository.get_by_season_id(season_id)
        if season_record and season_record.start_time:
            base_time = season_record.start_time
        else:
            default_time_string = '2021-01-01 00:00:00'
            base_time = strToDatetime(default_time_string)

        sub_time = getCountdown(cur_time, base_time)
        if sub_time > MAX_MODULUS:
            sub_time = MAX_MODULUS

        if time_reverse:
            sub_time = OFFSET_BASE_LIMIT - sub_time

        return sub_time

    # 批量更新榜单通用方法
    @classmethod
    def update_many_to_rank(cls, season_id, role_dict, rank_key):
        """
        批量更新榜单
        :param role_dict: {rid: {'ServerId': server_id, 'Score': 0}}
        :param rank_key: 榜单key
        :return:
        """
        sub_time = cls.get_sub_time(season_id)

        # 写入榜单
        data_list = list()  # [[score, fighter_power, rid, server_id], [score1, score2, value, value2]]
        for rid, role_info in role_dict.iteritems():
            info_list = []
            info_list.extend([role_info['Score'], sub_time, rid, role_info['ServerId']])
            data_list.append(info_list)
        normal_rank.add_list_to_rank_by_two_value(rank_key, data_list)

    @staticmethod
    def get_season_msg_from_group_rank_key(rank_key):
        """
        分赛区榜单key获取赛季信息
        :param rank_key:
        :return:
        """
        split_rank_key = rank_key.split(':')
        season_id, stage_id, group_id = split_rank_key[3], split_rank_key[4], split_rank_key[5]
        return season_id, stage_id, group_id

    @staticmethod
    def get_group_rank_key_list(season_id, stage_id):
        """
        获取分赛区榜单所有key
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :return:
        """
        key_list = []
        season_hash_cache = CrossSeasonHashCache()
        max_group_id = season_hash_cache.get_season_stage_max_group_id(season_id, stage_id)
        if max_group_id:
            max_group_id = int(max_group_id)
            for group_id in range(1, max_group_id+1):
                key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                        stage_id=stage_id, group_id=group_id)
                key_list.append(key)
        return key_list

    # ----------------------赛季总榜----------------------
    @classmethod
    def update_season_rank(cls, rid, server_id, season_id, score):
        """
        更新赛季总榜分数
        :param rid:
        :param server_id:
        :param season_id: 赛季id
        :param score: 总分数（覆盖性）
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)
        sub_time = cls.get_sub_time(season_id)
        normal_rank.update_progress(rid, rank_key, score, sub_time, server_id)

    @classmethod
    def update_many_to_season_rank(cls, role_dict, season_id):
        """
        批量更新榜单
        :param role_dict: {rid: {'ServerId': server_id, 'Score': 0}}
        :param season_id: 赛季id
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)

        cls.update_many_to_rank(season_id, role_dict, rank_key)

    @staticmethod
    def get_range_season_rank(season_id, form, start=0, end=499, need_dan=True, need_zero_score=False):
        """
        获取赛季总榜
        :param season_id: 赛季id
        :param form: 榜单格式
        :param start:
        :param end:
        :param need_dan: 是否需要获取玩家当前段位
        :param need_zero_score: 是否下发分数小于等于0的玩家
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)
        rank_list = normal_rank.get_rank_range(rank_key=rank_key, start=start, end=end, form=form,
                                               rank_type=normal_rank.RankType.RoleRank, season_id=season_id,
                                               need_dan=need_dan, need_zero_score=need_zero_score)
        return rank_list

    @staticmethod
    def get_season_rank_score(rid, server_id, season_id, need_zero_rank=False):
        """
        获取赛季总榜个人排名和积分
        :param rid:
        :param server_id:
        :param season_id:
        :param need_zero_rank: 积分小于等于0时,是否获取榜单排名,若为False且score<=0,则返回rank=0
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)
        rank, score = normal_rank.get_self_rank_and_score(rank_key, rid, server_id, need_zero_rank=need_zero_rank)
        return rank, score

    @staticmethod
    def get_season_range_by_score(season_id, max_score='+inf', min_score=0, start=None, num=None, with_score=False):
        """
        获取min和max之间的成员
        从大到小
        :param season_id: 赛季id
        :param max_score: 最大分,+inf表示无上限
        :param min_score: 最低分
        :param start: 开始下标
        :param num: 获取人数，为None时获取所有, start和num必须同时传参
        :param with_score: 是否需要成员分数
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)
        return normal_rank.get_set_range_by_score(rank_key, max_score, min_score, start, num, with_score)

    @staticmethod
    def del_season_rank(season_id):
        """
        删除赛季总榜单(赛季结束后清除数据使用)
        :param season_id: 赛季id
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)
        return normal_rank.delete_whole_rank(rank_key)

    # ----------------------分赛区榜----------------------
    @classmethod
    def update_season_group_rank(cls, rid, server_id, season_id, stage_id, group_id, score):
        """
        更新分赛区榜单分数
        :param rid:
        :param server_id:
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :param score: 总分数（覆盖性）
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id)
        sub_time = cls.get_sub_time(season_id)
        normal_rank.update_progress(rid, rank_key, score, sub_time, server_id)

    @classmethod
    def update_many_to_season_group_rank(cls, role_dict, season_id, stage_id, group_id):
        """
        批量更新榜单
        :param role_dict: {rid: {'ServerId': server_id, 'Score': 0}}
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id)
        cls.update_many_to_rank(season_id, role_dict, rank_key)

    @staticmethod
    def get_range_season_group_rank(season_id, stage_id, group_id, form, start=0, end=499, need_dan=True):
        """
        获取分赛区榜单
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :param form: 榜单格式
        :param start:
        :param end:
        :param need_dan: 是否需要获取玩家当前段位
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id)
        rank_list = normal_rank.get_rank_range(rank_key=rank_key, start=start, end=end, form=form,
                                               rank_type=normal_rank.RankType.RoleRank, season_id=season_id,
                                               need_dan=need_dan)
        return rank_list

    @staticmethod
    def get_season_group_rank_score(rid, server_id, season_id, stage_id, group_id):
        """获取分赛区榜个人排名和积分"""
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id)
        rank, score = normal_rank.get_self_rank_and_score(rank_key, rid, server_id)
        return rank, score

    @staticmethod
    def get_group_range_by_score(season_id, stage_id, group_id, max_score='+inf', min_score=0, start=None, num=None,
                               with_score=False):
        """
        获取min和max之间的成员
        从大到小
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :param max_score: 最大分,+inf表示无上限
        :param min_score: 最低分
        :param start: 开始下标
        :param num: 获取人数，为None时获取所有, start和num必须同时传参
        :param with_score: 是否需要成员分数
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id)
        return normal_rank.get_set_range_by_score(rank_key, max_score, min_score, start, num, with_score)

    @staticmethod
    def del_group_rank_one_member(season_id, stage_id, group_id, del_rid, del_server_id):
        """删除分赛区榜某位玩家"""
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id)
        normal_rank.delete_record_from_rank(del_rid, rank_key, del_server_id)

    @classmethod
    def del_season_all_group_rank(cls, season_id):
        """
        删除赛季所有阶段分赛区榜单(赛季结束后清除数据使用)
        :param season_id: 赛季id
        :return:
        """
        season_record = CrossSeasonActivityRepository.get_by_season_id(season_id)
        if (not season_record) or (not season_record.season_schedule):
            return
        max_stage_id = len(season_record.season_schedule)
        stage_id_list = [i for i in range(1, max_stage_id+1)]
        for stage_id in stage_id_list:
            group_rank_key_list = cls.get_group_rank_key_list(season_id, stage_id)
            normal_rank.delete_many_rank(group_rank_key_list)

    # ----------------------分赛区每日涨幅榜----------------------
    @classmethod
    def update_season_group_increase_rank(cls, rid, server_id, season_id, stage_id, group_id, day, score):
        """
        更新分赛区榜单分数
        :param rid:
        :param server_id:
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :param day: 赛季天数偏移量
        :param score: 总分数（覆盖性）
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupIncreaseRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        sub_time = cls.get_sub_time(season_id)
        normal_rank.update_progress(rid, rank_key, score, sub_time, server_id)

    @classmethod
    def update_many_to_season_group_increase_rank(cls, role_dict, season_id, stage_id, group_id, day):
        """
        批量更新榜单
        :param role_dict: {rid: {'ServerId': server_id, 'Score': 0}}
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :param day: 赛季天数偏移量
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupIncreaseRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        cls.update_many_to_rank(season_id, role_dict, rank_key)

    @staticmethod
    def get_season_group_increase_rank_score(rid, server_id, season_id, stage_id, group_id, day):
        """获取分赛区每日涨幅榜个人排名和积分"""
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupIncreaseRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        rank, score = normal_rank.get_self_rank_and_score(rank_key, rid, server_id)
        return rank, score

    @staticmethod
    def get_range_season_group_increase_rank(season_id, stage_id, group_id, day, form, start=0, end=9, need_dan=False):
        """
        获取分赛区每日涨幅榜
        :param season_id: 赛季id
        :param stage_id: 阶段id
        :param group_id: 分赛区id
        :param day: 赛季天数偏移量
        :param form: 榜单格式
        :param start:
        :param end:
        :param need_dan: 是否需要获取玩家当前段位
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupIncreaseRank, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        rank_list = normal_rank.get_rank_range(rank_key=rank_key, start=start, end=end, form=form,
                                               rank_type=normal_rank.RankType.RoleRank, season_id=season_id,
                                               need_dan=need_dan)
        return rank_list

    # ----------------------赛季总榜每日备份榜单----------------------
    @staticmethod
    def get_range_season_backup_rank(season_id, day, form, start=0, end=9, need_dan=False, need_zero_score=False):
        """
        获取赛季总榜每日备份榜单
        :param season_id: 赛季id
        :param day: 赛季天数偏移量
        :param form: 榜单格式
        :param start:
        :param end:
        :param need_dan: 是否需要获取玩家当前段位
        :param need_zero_score: 是否下发分数小于等于0的玩家
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRankBackup, season_id=season_id, day=day)
        rank_list = normal_rank.get_rank_range(rank_key=rank_key, start=start, end=end, form=form,
                                               rank_type=normal_rank.RankType.RoleRank, season_id=season_id,
                                               need_dan=need_dan, need_zero_score=need_zero_score)
        return rank_list

    @staticmethod
    def get_season_backup_rank_score(rid, server_id, season_id, day, need_zero_score=False):
        """
        获取总榜备份个人排名和积分
        :param rid:
        :param server_id:
        :param season_id:
        :param day:
        :param need_zero_score: 是否下发分数小于等于0的玩家
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRankBackup, season_id=season_id, day=day)
        rank, score = normal_rank.get_self_rank_and_score(rank_key, rid, server_id, need_zero_rank=need_zero_score)
        return rank, score

    @staticmethod
    def set_season_backup_expire_time(season_id, day, expire_time):
        """
        设置赛季总榜每日备份榜单过期时间（单位:s)
        :param season_id:
        :param day: 赛季便宜天数,用于拼接榜单key
        :param expire_time: 榜单过期时间
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRankBackup, season_id=season_id, day=day)
        normal_rank.set_expire_time(rank_key, expire_time)

    @classmethod
    def get_season_backup_rank_sub(cls, rid, server_id, season_id, larger_day, smaller_day):
        """
        获取全大区榜排名涨幅
        :param rid:
        :param server_id:
        :param season_id:
        :param larger_day: 较大天数
        :param smaller_day: 较小天数
        :return:
        """
        larger_day = max(larger_day, smaller_day)
        smaller_day = min(larger_day, smaller_day)
        larger_day_rank, _ = cls.get_season_backup_rank_score(rid, server_id, season_id, larger_day)
        smaller_day_rank, _ = cls.get_season_backup_rank_score(rid, server_id, season_id, smaller_day)
        return larger_day_rank - smaller_day_rank

    # ----------------------分赛区榜单每日备份榜单----------------------
    @staticmethod
    def get_range_season_group_backup_rank(season_id, stage_id, group_id, day, form, start=0, end=9, need_dan=False):
        """
        获取赛分赛区每日备份榜单
        :param season_id: 赛季id
        :param day: 赛季天数偏移量
        :param form: 榜单格式
        :param start:
        :param end:
        :param need_dan: 是否需要获取玩家当前段位
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRankBackup, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        rank_list = normal_rank.get_rank_range(rank_key=rank_key, start=start, end=end, form=form,
                                               rank_type=normal_rank.RankType.RoleRank, season_id=season_id,
                                               need_dan=need_dan)
        return rank_list

    @staticmethod
    def get_season_group_backup_rank_score(rid, server_id, season_id, stage_id, group_id, day):
        """获取分赛区榜备份个人排名和积分"""
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRankBackup, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        rank, score = normal_rank.get_self_rank_and_score(rank_key, rid, server_id)
        return rank, score

    @staticmethod
    def set_season_group_backup_expire_time(season_id, stage_id, group_id, day, expire_time):
        """
        设置分赛区榜单每日备份榜单过期时间（单位:s)
        :param season_id:
        :param stage_id:
        :param group_id:
        :param day:
        :param expire_time:
        :return:
        """
        rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRankBackup, season_id=season_id,
                                                     stage_id=stage_id, group_id=group_id, day=day)
        normal_rank.set_expire_time(rank_key, expire_time)

    @classmethod
    def get_season_group_backup_rank_sub(cls, rid, server_id, season_id, stage_id, group_id, larger_day, smaller_day):
        """
        获取分赛区榜排名涨幅
        :param rid:
        :param server_id:
        :param season_id:
        :param larger_day: 较大天数
        :param smaller_day: 较小天数
        :return:
        """
        larger_day = max(larger_day, smaller_day)
        smaller_day = min(larger_day, smaller_day)
        larger_day_rank, _ = cls.get_season_group_backup_rank_score(rid, server_id, season_id, stage_id, group_id,
                                                                    larger_day)
        smaller_day_rank, _ = cls.get_season_group_backup_rank_score(rid, server_id, season_id, stage_id, group_id,
                                                                     smaller_day)
        return larger_day_rank - smaller_day_rank

    @classmethod
    def del_all_group_rank(cls, season_id, stage_id):
        """
        删除阶段所有分赛区榜
        :param season_id:
        :param stage_id:
        :return:
        """
        group_rank_key_list = cls.get_group_rank_key_list(season_id, stage_id)
        normal_rank.delete_many_rank(group_rank_key_list)

    # ----------------------定时任务备份赛季总榜和分赛区榜----------------------
    def copy_season_rank(self):
        """
        备份赛季总榜和所有分赛区榜单
        注：若已备份则不会刷新已备份数据
        :return:
        """
        # 备份榜单过期时间(单位:s)
        expire_time = 345600  # 4天

        # TODO: 确认偏移的时间赛季信息获取是否正确
        offset_time = addTimes(getCT(), days=-1)
        season_id, stage_id, _ = get_season_base_info(current_time=offset_time)
        day = get_day_of_season(current_time=offset_time)
        if not day:
            # 未开启不备份
            return {'status': False, 'info': 'COPY_SEASON_RANK_ERROR:当前无开始赛季, day:{}'.format(day)}

        # 每日凌晨4点备份前一天
        # 所以榜单key上的day需要-1
        # day = day - 1
        if day < 1:
            # 备份操作从赛季开启第2天开始
            # 也就是第2天备份第1天
            # 第一天没有备份的操作
            return {'status': False, 'info': 'COPY_SEASON_RANK_ERROR:赛季第一天不做备份, season_id:{}, stage_id:{}, day:{}'.format(
                season_id, stage_id, day)}

        # 备份赛季总榜
        if season_id:
            # 获取榜单
            rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRank, season_id=season_id)

            # 生成备份榜单key
            copy_rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonRankBackup,
                                                              season_id=season_id, day=day)
            is_exists = normal_rank.is_exists(copy_rank_key)
            if not is_exists:
                # 若无备份记录,则备份榜单
                normal_rank.copy_rank(new_rank=copy_rank_key, old_rank=rank_key)

                # 设置备份榜单过期时间
                normal_rank.set_expire_time(copy_rank_key, expire_time=expire_time)

        # 备份分赛区榜单
        if season_id and stage_id and day:
            group_rank_list = self.get_group_rank_key_list(season_id, stage_id)
            for key in group_rank_list:
                # 生成备份榜单key
                season_id, stage_id, group_id = self.get_season_msg_from_group_rank_key(key)
                copy_group_rank_key = normal_rank.fill_leader_board_key(CrossSeasonRankKey.SeasonGroupRankBackup,
                                                                        season_id=season_id, stage_id=stage_id,
                                                                        group_id=group_id, day=day)
                is_exists = normal_rank.is_exists(copy_group_rank_key)
                if not is_exists:
                    # 若无备份记录,则备份榜单
                    normal_rank.copy_rank(new_rank=copy_group_rank_key, old_rank=key)

                    # 设置备份榜单过期时间
                    normal_rank.set_expire_time(copy_group_rank_key, expire_time=expire_time)

        return {'status': True, 'RetInfo': 'COPY_SEASON_RANK_OK: 每日备份榜单season_id:{}, stage_id:{}, day:{}'.format(
            season_id, stage_id, day)}


season_rank_utils = SeasonRankUtils()
